/// A owned-or-ref enum
#[derive(Debug, Eq)]
pub enum OwnedOrRef<'a, T> {
  Borrowed(&'a T),
  Owned(T),
}

impl<'a, 'b, A, B> PartialEq<OwnedOrRef<'a, A>> for OwnedOrRef<'b, B>
where
  B: PartialEq<A>,
{
  #[inline]
  fn eq(&self, other: &OwnedOrRef<'a, A>) -> bool {
    PartialEq::eq(self.as_ref(), other.as_ref())
  }
}

impl<T> From<T> for OwnedOrRef<'_, T> {
  fn from(value: T) -> Self {
    Self::Owned(value)
  }
}

impl<'a, T> From<&'a T> for OwnedOrRef<'a, T> {
  fn from(value: &'a T) -> Self {
    Self::Borrowed(value)
  }
}

impl<T> AsRef<T> for OwnedOrRef<'_, T> {
  fn as_ref(&self) -> &T {
    match self {
      Self::Borrowed(b) => b,
      Self::Owned(o) => o,
    }
  }
}

impl<T> OwnedOrRef<'_, T> {
  pub fn into_owned(self) -> T {
    match self {
      Self::Borrowed(_) => panic!("Borrowed data does not allow call into_owned"),
      Self::Owned(o) => o,
    }
  }
}

// Impl rkyv feature
// The following code is generated by
// ```
// use rkyv::{Archive, Deserialize, Serialize};
//
// #[derive(Archive, Deserialize, Serialize)]
// struct OwnedOrRef<F: Archive>(F);
// ```
// and use the previous OwnedOrRef implementation.
use rkyv::{
  Archive, Deserialize, Serialize,
  bytecheck::{CheckBytes, TupleStructCheckContext, rancor},
  rancor::Fallible,
};

pub struct ArchivedOwnedOrRef<T: Archive>(T::Archived);

unsafe impl<T, C> CheckBytes<C> for ArchivedOwnedOrRef<T>
where
  T: Archive,
  C: Fallible + ?Sized,
  <C as Fallible>::Error: rancor::Trace,
  <T as Archive>::Archived: CheckBytes<C>,
{
  unsafe fn check_bytes(value: *const Self, context: &mut C) -> Result<(), <C as Fallible>::Error> {
    unsafe {
      <<T as Archive>::Archived as CheckBytes<C>>::check_bytes(&raw const (*value).0, context)
        .map_err(|e| {
          <<C as Fallible>::Error as rancor::Trace>::trace(
            e,
            TupleStructCheckContext {
              tuple_struct_name: "ArchivedOwnedOrRef",
              field_index: 0usize,
            },
          )
        })?;
    }
    Ok(())
  }
}

pub struct OwnedOrRefResolver<T: Archive>(<T as Archive>::Resolver);

impl<'a, F> Archive for OwnedOrRef<'a, F>
where
  F: Archive,
{
  type Archived = ArchivedOwnedOrRef<F>;
  type Resolver = OwnedOrRefResolver<F>;

  fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
    let field_ptr = unsafe { &raw mut (*out.ptr()).0 };
    let field_out = unsafe { rkyv::Place::from_field_unchecked(out, field_ptr) };
    <F as Archive>::resolve(self.as_ref(), resolver.0, field_out);
  }
}
unsafe impl<F> rkyv::traits::Portable for ArchivedOwnedOrRef<F>
where
  F: Archive,
  <F as Archive>::Archived: rkyv::traits::Portable,
{
}

impl<'a, F, D: Fallible + ?Sized> Deserialize<OwnedOrRef<'a, F>, D>
  for rkyv::Archived<OwnedOrRef<'a, F>>
where
  F: Archive,
  <F as Archive>::Archived: Deserialize<F, D>,
{
  fn deserialize(&self, deserializer: &mut D) -> Result<OwnedOrRef<'a, F>, <D as Fallible>::Error> {
    Ok(OwnedOrRef::Owned(
      <<F as Archive>::Archived as Deserialize<F, D>>::deserialize(&self.0, deserializer)?,
    ))
  }
}

impl<'a, F, S: Fallible + ?Sized> Serialize<S> for OwnedOrRef<'a, F>
where
  F: Serialize<S>,
{
  fn serialize(
    &self,
    serializer: &mut S,
  ) -> Result<<Self as Archive>::Resolver, <S as Fallible>::Error> {
    Ok(OwnedOrRefResolver(<F as Serialize<S>>::serialize(
      self.as_ref(),
      serializer,
    )?))
  }
}
